import {
  createDBConnection,
  deepClone,
  IState,
  Logger,
  RpcConnection,
  SceneIdEnum,
  ServerEnum,
  ServerPort,
} from "../../common";
import { Connection } from "mysql";
import { CHILD_PROCESS_READY } from "../utils";

const ACTOR_SPEED = 120;

export abstract class Scene {
  abstract sceneId: SceneIdEnum;
  connection: Connection;
  game: RpcConnection;
  logger: Logger;

  state: IState = {
    actors: [],
  };
  prevState: any = {};
  pendingInput: any[] = [];

  /***
   * 作为一个客户端连接Game服务器
   */
  async init() {
    const sceneName = `Scene${this.sceneId}`;
    this.connection = await createDBConnection();
    this.logger = new Logger(sceneName);

    this.game = await RpcConnection.Create({
      netOptions: {
        port: ServerPort.Game,
        host: "localhost",
      },
      implement: this,
      logger: this.logger,
    });

    const { error } = await this.game.call({
      name: "registerServer",
      params: [ServerEnum.Scene, this.sceneId],
    });

    if (error) {
      this.logger.fatal(error);
      return;
    }

    setInterval(() => {
      this.stateFromServer();
    }, 70);

    // @ts-ignore
    // ipc通信，通知父进程初始化完成
    process.send(CHILD_PROCESS_READY);

    this.logger.info(`${sceneName}服务启动！`);
  }

  enterActor(account: string, actorId: number) {
    return new Promise<void>((resolve, reject) => {
      this.connection.query(
        `select id, scene_id, account, nickname from actor where account = ? and id = ? `,
        [account, actorId],
        (err, result) => {
          if (err) {
            reject(err.message);
            return;
          }

          const target = result?.[0];
          if (!target) {
            reject("no actor");
            return;
          }

          const actor = {
            id: target.id,
            account: target.account,
            nickname: target.nickname,
            posX: 0,
            posY: 0,
            sceneId: target.scene_id,
          };
          this.state.actors.push(actor);
          this.prevState = deepClone(this.state);
          resolve();
        }
      );
    });
  }

  leaveActor(actorId: number) {
    const index = this.state.actors.findIndex((e) => e.id === actorId);
    if (index > -1) {
      this.state.actors.splice(index, 1);
      this.prevState = deepClone(this.state);
    }
  }

  inputFromClient(actorId: number, data: any) {
    if (this.pendingInput.length === 0) {
      this.prevState = deepClone(this.state);
    }
    this.pendingInput.push(data);
    const actor = this.state.actors.find((e) => e.id === actorId);
    if (!actor) {
      return;
    }
    const { directionX, directionY, dt } = data;
    actor.posX += directionX * ACTOR_SPEED * dt;
    actor.posY += directionY * ACTOR_SPEED * dt;
  }

  stateFromServer() {
    const accounts = this.state.actors.map((e) => e.account);
    if (!accounts.length) {
      return;
    }
    const input = this.pendingInput;
    this.pendingInput = [];
    this.game.send({
      name: "stateFromServer",
      params: [
        accounts,
        {
          state: this.prevState,
          input,
        },
      ],
    });
    this.prevState = deepClone(this.state);
  }
}
